package pac

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	db "slgw_web/application/gormdb"
	"slgw_web/serial/util"
	"strconv"
	"strings"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
	models "openzbox.com/frame/echo/models/sl_gw01"
)

// MqttConnectConfig 连接的相关配置
// var Config MqttConnectConfig

type MqttConnectConfig struct {
	Name              string //连接的name 无用
	Broker            string
	User              string
	Password          string
	Certificate       string //证书文件
	PrivateKey        string //秘钥
	ClientId          string
	WillEnabled       bool          //遗愿
	WillTopic         string        //遗愿主题
	WillPayload       string        //遗愿消息
	WillQos           byte          //遗愿服务质量
	Qos               byte          //服务质量
	Retained          bool          //保留消息
	keepAliveInterval time.Duration //心跳
	OnConnect         mqtt.OnConnectHandler
	OnConnectionLost  mqtt.ConnectionLostHandler
}

func NewTlsConfig(certFile string) (*tls.Config, error) {
	certpool := x509.NewCertPool()
	// ca, err := os.ReadFile(certFile)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// }
	ca := []byte("sb")
	certpool.AppendCertsFromPEM(ca)
	return &tls.Config{
		RootCAs:            certpool,
		ClientAuth:         tls.NoClientCert,
		ClientCAs:          nil,
		InsecureSkipVerify: true,
	}, nil
}
func NewMqttClient(config MqttConnectConfig) (*mqtt.Client, error) {
	var client *mqtt.Client
	//保活周期，单位为秒

	opts := mqtt.NewClientOptions().AddBroker(config.Broker).SetClientID(config.ClientId).SetMaxReconnectInterval(time.Second * 10).SetKeepAlive(config.keepAliveInterval) // 设置保活周期
	if config.WillEnabled {
		opts.SetWill(config.WillTopic, config.WillPayload, config.WillQos, config.Retained)
	}
	//判断是否设置证书
	if config.Certificate != "" {
		// tlsConfig, err := newTLSConfig(config.Certificate, config.PrivateKey)
		tlsConfig, err := NewTlsConfig(config.Certificate)
		if err != nil {
			return client, err
		}
		opts.SetTLSConfig(tlsConfig)
	}
	opts.SetUsername(config.User).SetPassword(config.Password)
	c := mqtt.NewClient(opts)
	// 用token的状态判断
	if tc := c.Connect(); tc.Wait() && tc.Error() != nil {

		return client, tc.Error()
	}
	client = &c
	return client, nil
}

// Publish  Mqtt message.
func Publish(payload string, MQTTUsername string, MQTTClientID string, DeviceInfo models.DeviceInfo) {
	// topic := "$sys/" + MQTTUsername + "/" + MQTTClientID + "/thing/property/post"
	topic := *DeviceInfo.MQTTPublish
	mc := (*DeviceInfo.MQTTClient)
	if mc != nil {
		if mc.IsConnected() {
			time.Sleep(2 * time.Second)
			if tc := mc.Publish(topic, 0, false, payload); tc.Wait() && tc.Error() != nil {
				util.Error("MQTTUsername:" + MQTTUsername + ",MQTTClientID:" + MQTTClientID + ",mqtt发布错误:" + tc.Error().Error())
			}
		} else {
			ReMqttThd(DeviceInfo)
		}

	}

}

// 重新连接mqtt
func ReMqttThd(DeviceInfo models.DeviceInfo) {
	util.Error("连接断开，尝试重新连接...")
	//(*DeviceInfo.MQTTClient).Unsubscribe("$sys/" + *DeviceInfo.MQTTUsername + "/" + *DeviceInfo.MQTTClientID + "/thing/property/post/reply") // 取消订阅的主题
	(*DeviceInfo.MQTTClient).Unsubscribe(*DeviceInfo.MQTTSubscribe)
	(*DeviceInfo.MQTTClient).Disconnect(250) // 带有超时的断开连接
	mqttConnectConfig := MqttConnectConfig{}
	currentTimestamp := time.Now().AddDate(10, 0, 0).Unix()
	et := strconv.FormatInt(currentTimestamp, 10)
	s, _ := GenerateSign(et, *DeviceInfo.MQTTAccessKey, *DeviceInfo.MQTTUsername, *DeviceInfo.MQTTClientID)
	sign := url.QueryEscape(s)
	password := "version=2018-10-31&res=products%2F" + *DeviceInfo.MQTTUsername + "%2Fdevices%2F" + *DeviceInfo.MQTTClientID + "&et=" + et + "&method=md5&sign=" + sign
	mqttConnectConfig.Name = DeviceInfo.Name
	mqttConnectConfig.Broker = *DeviceInfo.MQTTHostName + ":" + *DeviceInfo.MQTTPort
	mqttConnectConfig.User = *DeviceInfo.MQTTUsername
	mqttConnectConfig.Password = password
	mqttConnectConfig.Certificate = "E:/kaifa/GO/Gopath/DTUGW03/src/idcard_Demo/MQTTS-certificate.pem" //证书文件
	mqttConnectConfig.ClientId = *DeviceInfo.MQTTClientID
	mqttConnectConfig.WillEnabled = false //遗愿
	mqttConnectConfig.WillTopic = "0"     //遗愿主题
	mqttConnectConfig.WillPayload = "0"   //遗愿消息
	mqttConnectConfig.WillQos = 0         //遗愿服务质量
	mqttConnectConfig.Qos = 0             //服务质量
	mqttConnectConfig.Retained = false    //保留消息
	mqttConnectConfig.keepAliveInterval = time.Duration(DeviceInfo.OHRT) * time.Second
	//Config = mqttConnectConfig
	mc, err := NewMqttClient(mqttConnectConfig)
	if err != nil {
		util.Error("名称:" + DeviceInfo.Name + ",MQTTS重新连接错误:" + err.Error() + ",将重启2次(每10s一次)")
		for i := 1; i < 3; i++ {
			time.Sleep(10 * time.Second)
			mc, err = NewMqttClient(mqttConnectConfig)
			if err != nil {
				util.Error("名称:" + mqttConnectConfig.Name + ",第" + strconv.Itoa(i) + "次重连失败:" + err.Error())
			} else {
				util.Error("名称:" + mqttConnectConfig.Name + ",第" + strconv.Itoa(i) + "次重连成功")
				DeviceInfo.MQTTClient = mc
				//util.MqttClient = mc
			}

		}
	} else {
		DeviceInfo.MQTTClient = mc
		//util.MqttClient = mc
	}
	if mc != nil {
		// Subscribe("$sys/"+*DeviceInfo.MQTTUsername+"/"+*DeviceInfo.MQTTClientID+"/thing/property/post/reply", DeviceInfo.MQTTClient)
		Subscribe(*DeviceInfo.MQTTSubscribe, DeviceInfo.MQTTClient)
	}
}

func Subscribe(topic string, mc *mqtt.Client) {
	token := (*mc).Subscribe(topic, 0, func(c mqtt.Client, msg mqtt.Message) {

		if len(msg.Payload()) > 0 {
			fmt.Println("Payload", fmt.Sprintf("%s", msg.Payload()))

			if util.IsRereported == 1 {
				//go func() {
				time.Sleep(100 * time.Millisecond)

				Payload := util.Payload
				// 将 JSON 数据解析到Payload
				err := json.Unmarshal(msg.Payload(), &Payload)
				if err != nil {
					util.Error("mqtt订阅消息解析失败,msg:" + fmt.Sprintf("%s", msg.Payload()) + ",err:" + err.Error())
				} else {
					if Payload.Code == 200 {
						db := db.GetDB()
						var collectData models.CollectData
						// result := db.Where("MqttReportedID = ? ", Payload.Id).Find(&collectData)
						db.Where("MqttReportedID = ? ", Payload.Id).Delete(&collectData)
						// if result.Error == nil {
						// 	//有值,删除
						// 	db.Delete(&collectData)
						// }
					} else {
						util.Error("mqtt订阅消息返回失败,msg:" + fmt.Sprintf("%s", msg.Payload()))
					}
				}
				//}()
			}

		}
	})
	if token.Error() != nil {
		util.Error("mqtt订阅消息失败,msg:" + token.Error().Error())
	}
	token.Wait()
	util.Error(fmt.Sprintf("订阅主题[%s]成功", topic))
}

// 登录mqtts
func MqttThd(DeviceInfo models.DeviceInfo) *mqtt.Client {
	var mc *mqtt.Client
	var err error
	if DeviceInfo.MQTTSubscribe == nil {
		util.Error("mqtt连接失败,Subscribe未填写,Name:" + DeviceInfo.Name)
		return mc
	}
	if DeviceInfo.MQTTPublish == nil {
		util.Error("mqtt连接失败,Publish未填写,Name:" + DeviceInfo.Name)
		return mc
	}
	mqttConnectConfig := MqttConnectConfig{}

	currentTimestamp := time.Now().AddDate(10, 0, 0).Unix()
	et := strconv.FormatInt(currentTimestamp, 10)
	s, _ := GenerateSign(et, *DeviceInfo.MQTTAccessKey, *DeviceInfo.MQTTUsername, *DeviceInfo.MQTTClientID)
	sign := url.QueryEscape(s)
	password := "version=2018-10-31&res=products%2F" + *DeviceInfo.MQTTUsername + "%2Fdevices%2F" + *DeviceInfo.MQTTClientID + "&et=" + et + "&method=md5&sign=" + sign
	//fmt.Println("password", password)
	//fmt.Println("et", et)
	mqttConnectConfig.Name = DeviceInfo.Name
	mqttConnectConfig.Broker = *DeviceInfo.MQTTHostName + ":" + *DeviceInfo.MQTTPort
	mqttConnectConfig.User = *DeviceInfo.MQTTUsername
	mqttConnectConfig.Password = password
	mqttConnectConfig.Certificate = "sb" //证书文件
	mqttConnectConfig.ClientId = *DeviceInfo.MQTTClientID
	mqttConnectConfig.WillEnabled = false //遗愿
	mqttConnectConfig.WillTopic = "0"     //遗愿主题
	mqttConnectConfig.WillPayload = "0"   //遗愿消息
	mqttConnectConfig.WillQos = 0         //遗愿服务质量
	mqttConnectConfig.Qos = 0             //服务质量
	mqttConnectConfig.Retained = false    //保留消息
	mqttConnectConfig.keepAliveInterval = time.Duration(DeviceInfo.OHRT) * time.Second

	//Config = mqttConnectConfig
	mc, err = NewMqttClient(mqttConnectConfig)
	if err != nil {
		util.Error("名称:" + DeviceInfo.Name + ",MQTTS连接错误:" + err.Error() + ",将重启10次(每10s一次),请检查配置是否正确")
		for i := 1; i < 11; i++ {
			time.Sleep(10 * time.Second)
			mc, err = NewMqttClient(mqttConnectConfig)
			if err != nil {
				util.Error("名称:" + DeviceInfo.Name + ",第" + strconv.Itoa(i) + "次重连失败:" + err.Error())
			} else {
				util.Error("名称:" + DeviceInfo.Name + ",第" + strconv.Itoa(i) + "次重连成功")
			}

		}
	}
	if mc != nil {
		//Subscribe("$sys/"+*DeviceInfo.MQTTUsername+"/"+*DeviceInfo.MQTTClientID+"/thing/property/post/reply", mc)
		Subscribe(*DeviceInfo.MQTTSubscribe, mc)
	}
	return mc
}
func GenerateSign(et string, access_key string, MQTTUsername string, MQTTClientID string) (string, error) {

	params_map := map[string]string{
		"et":      et,
		"method":  "md5",
		"res":     "products/" + MQTTUsername + "/devices/" + MQTTClientID,
		"version": "2018-10-31",
	}
	signature_str := params_map["et"] + "\n" + params_map["method"] + "\n" + params_map["res"] + "\n" + params_map["version"]
	hmac_str := ""
	switch strings.ToLower(params_map["method"]) {
	case "sha1":
		hmac_str = HmacSha1(signature_str, Base64Decode(access_key))
	case "sha256":
		hmac_str = HmacSha256(signature_str, Base64Decode(access_key))
	case "md5":
		hmac_str = HmacMd5(signature_str, Base64Decode(access_key))
	default:
		return "", errors.New("签名参数错误！")
	}

	sign := Base64Encode(hmac_str)
	return sign, nil
}

func Base64Encode(message string) string {
	return base64.StdEncoding.EncodeToString([]byte(message))
}

func Base64Decode(message string) string {
	decode_str, err := base64.StdEncoding.DecodeString(message)
	if err != nil {
		return ""
	}
	return string(decode_str)
}

func HmacSha256(data string, secret string) string {
	h := hmac.New(sha256.New, []byte(secret))
	h.Write([]byte(data))
	return string(h.Sum(nil))
	//return hex.EncodeToString(h.Sum(nil))
}

func HmacMd5(data string, secret string) string {
	h := hmac.New(md5.New, []byte(secret))
	h.Write([]byte(data))
	return string(h.Sum(nil))
	//return hex.EncodeToString(h.Sum(nil))
}

func HmacSha1(data string, secret string) string {
	h := hmac.New(sha1.New, []byte(secret))
	h.Write([]byte(data))
	return string(h.Sum(nil))
	//return hex.EncodeToString(h.Sum(nil)) //不需以十六进制字符串输出
}

func TaskReportedMqttData(sb models.DeviceInfo, types int) {
	device_ID := sb.ID
	deviceInfoID := sb.DeviceInfoID
	var collectDataList = util.CollectDataList        //采集到的信息
	var deviceDetailList = util.DeviceInfo_DetailList //对应的上报点位
	times := time.Now().Format("2006-01-02 15:04:05")
	currentTimestamps := time.Now().Unix()
	TMs := strconv.FormatInt(currentTimestamps, 10)
	mqttReportedIDs := TMs + strconv.Itoa(time.Now().Second())

	params := make(map[string]interface{})

	if len(collectDataList) > 0 && len(deviceDetailList) > 0 {
		for _, serialSlaveDetail := range collectDataList {
			for _, DeviceInfoDetailListData := range deviceDetailList {
				if sb.ID == DeviceInfoDetailListData.Device_ID {
					if serialSlaveDetail.ID == DeviceInfoDetailListData.Serial_Slave_Detail_ID { //如果采集到的是需要上报的点位
						currentTimestamp := time.Now().Unix()
						TM := strconv.FormatInt(currentTimestamp, 10)            //当前时间戳
						value := serialSlaveDetail.Value2                        //采集到的数据
						mqttReportedID := TM + strconv.Itoa(time.Now().Second()) //上报的唯一id
						if value != nil {
							result, err := strconv.ParseFloat(*value, 64)
							if err != nil {
								util.Error("转换失败:", err)
								return
							}

							if types == 1 { //定时上报
								//存入数据库
								if sb.ApplicationLayerProtocol == 3 {
									resultStr := fmt.Sprintf("%.3f", result)
									// fmt.Println("resultStr", resultStr)
									collectData := "{\"id\":\"" + mqttReportedID + "\",\"version\":\"1.0\",\"params\":{\"ACCW\":{\"value\":" + resultStr + "},\"MPCD\":{\"value\":\"" + DeviceInfoDetailListData.MPCD + "\"},\"STCD\":{\"value\":\"" + DeviceInfoDetailListData.STCD + "\"},\"TM\":{\"value\":" + TM + "000" + "}}}"

									Rereported(device_ID, deviceInfoID, collectData, times, mqttReportedID, sb)
									Publish(collectData, *sb.MQTTUsername, *sb.MQTTClientID, sb)
								} else if sb.ApplicationLayerProtocol == 4 {

									//float32
									if DeviceInfoDetailListData.MqttDataType == 1 || DeviceInfoDetailListData.MqttDataType == 2 {
										step := 0
										if DeviceInfoDetailListData.MqttStep != nil {
											step = *DeviceInfoDetailListData.MqttStep

										}
										value1, err := strconv.ParseFloat(*value, 64)
										if err != nil {
											util.Error("上传标识：" + DeviceInfoDetailListData.MqttKey + ",上传参数:" + *value + "浮点型转换失败,error:" + err.Error())
										} else {
											valuestr := fmt.Sprintf("%."+strconv.Itoa(step)+"f", value1)
											valueFlaot, err := strconv.ParseFloat(valuestr, 64)
											if err != nil {
												util.Error("上传标识：" + DeviceInfoDetailListData.MqttKey + ",上传参数:" + *value + "浮点型转换失败,error:" + err.Error())
											} else {
												params[DeviceInfoDetailListData.MqttKey] = map[string]interface{}{"value": valueFlaot}
											}
										}

									} else if DeviceInfoDetailListData.MqttDataType == 3 {
										valueInt, err := strconv.Atoi(*value)
										if err != nil {
											util.Error("上传标识：" + DeviceInfoDetailListData.MqttKey + ",上传参数:" + *value + "int类型转换失败,error:" + err.Error())
										} else {
											params[DeviceInfoDetailListData.MqttKey] = map[string]interface{}{"value": valueInt}
										}
									} else if DeviceInfoDetailListData.MqttDataType == 4 {
										params[DeviceInfoDetailListData.MqttKey] = map[string]interface{}{"value": *value}
									}

								}
							}
						}
						//time.Sleep(2 * time.Second) //隔两秒上报下一条

					} else if DeviceInfoDetailListData.Serial_ID < 0 {
						tm := currentTimestamps
						if DeviceInfoDetailListData.Serial_ID == -2 {
							tm = currentTimestamps * 1000
						}
						params[DeviceInfoDetailListData.MqttKey] = map[string]interface{}{"value": tm}
					}
				}
			}
		}

	}
	if sb.ApplicationLayerProtocol == 4 {
		// 创建一个Data结构体实例并填充数据
		collectDatas := struct {
			ID      string                 `json:"id"`
			Version string                 `json:"version"`
			Params  map[string]interface{} `json:"params"`
		}{
			ID:      mqttReportedIDs,
			Version: "1.0",
			Params:  params,
		}
		// 将结构体转换成JSON字符串
		jsonData, err := json.Marshal(collectDatas)
		if err != nil {
			fmt.Println("JSON编码错误:", err)
			return
		}

		//fmt.Println("collectDatas", collectDatas)
		// 打印JSON字符串
		//fmt.Println("string(jsonData)", string(jsonData))
		data := string(jsonData)
		// 使用strconv.Quote将JSON字符串中的双引号进行转义
		// escapedJSON := strconv.Quote(string(jsonData))
		// fmt.Println("escapedJSON", escapedJSON)
		Rereported(device_ID, deviceInfoID, data, times, mqttReportedIDs, sb)
		Publish(data, *sb.MQTTUsername, *sb.MQTTClientID, sb)
	}
}

// //如果开启断点重传功能，就把历史记录保存，并且每次发送前判断是否有需要续传的数据记录
func Rereported(device_ID int64, deviceInfoID string, escapedJSON string, times string, mqttReportedIDs string, sb models.DeviceInfo) {
	db := db.GetDB()
	if util.IsRereported == 1 {

		CollectDatas := models.CollectData{
			Device_ID:      device_ID,
			DeviceInfoID:   deviceInfoID,
			Data:           escapedJSON,
			CreateTime:     times,
			MqttReportedID: mqttReportedIDs,
		}
		db.Create(&CollectDatas)

		if len(util.RereportedTag) > 0 { //判断是否需要查询数据库续传
			RereportedDay := -1
			RI := 0
			for RId, RItem := range util.RereportedTag {
				if RItem.DeviceInfo_ID == sb.ID {
					RereportedDay = RItem.RereportedDay
					RI = RId
					break
				}
			}
			if RereportedDay != -1 {
				if RereportedDay != time.Now().Day() { //如果上次续传的日期是今天，就不再续传 等待下次
					//go func() {
					util.RereportedTag[RI].RereportedDay = time.Now().Day()
					time.Sleep(2 * time.Second) //延时 防止粘包
					var collectDataList []models.CollectData
					db.Where("Device_ID = ? ", sb.ID).Find(&collectDataList)
					//fmt.Println("collectDataList", collectDataList)
					if len(collectDataList) > 0 {
						//把上传失败的重新上传
						for _, item := range collectDataList {
							Publish(item.Data, *sb.MQTTUsername, *sb.MQTTClientID, sb)
							time.Sleep(2 * time.Second)
						}
					}
					//}()
					// //删除3天前的数据
					k := time.Now()
					d, _ := time.ParseDuration("-24h")
					s := k.Add(3 * d).Format("2006-01-02 00:00:00")
					result := db.Where("CreateTime<=?", s).Delete(&models.CollectData{})
					rows := result.RowsAffected //删除的条数
					if rows > 0 {
						db.Exec("VACUUM") //释放空闲的占用
					}
				}
			}
		}

	}
}
